package top.zlcode.judge.service.impl;

import com.alibaba.fastjson2.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import top.zlcode.api.judeg.domain.UserExeResult;
import top.zlcode.api.judeg.domain.dto.JudgeSubmitDTO;
import top.zlcode.api.judeg.domain.vo.UserQuestionResultVO;
import top.zlcode.common.core.constants.Constants;
import top.zlcode.common.core.constants.JudgeConstants;
import top.zlcode.common.core.enums.CodeRunStatus;
import top.zlcode.judge.domain.SandBoxExecuteResult;
import top.zlcode.judge.domain.UserSubmit;
import top.zlcode.judge.mapper.UserSubmitMapper;
import top.zlcode.judge.service.IJudgeService;
import top.zlcode.judge.service.ISandBoxPoolService;
import top.zlcode.judge.service.ISandBoxService;

import java.util.ArrayList;
import java.util.List;

@Service
public class JudgeServiceImpl implements IJudgeService {

    @Autowired
    private ISandBoxService sandBoxService;

    @Autowired
    private ISandBoxPoolService sandBoxPoolService;

    @Autowired
    private UserSubmitMapper userSubmitMapper;

    @Override
    public UserQuestionResultVO doJudgeJavaCode(JudgeSubmitDTO judgeSubmitDTO) {

        // 代码沙箱执行代码, 拿到返回值
        SandBoxExecuteResult sandBoxExecuteResult =
                sandBoxPoolService.exeJavaCode(judgeSubmitDTO.getUserId(), judgeSubmitDTO.getUserCode(), judgeSubmitDTO.getInputList());

        // 用户答题响应VO
        UserQuestionResultVO userQuestionResultVO = new UserQuestionResultVO();

        if (sandBoxExecuteResult != null && CodeRunStatus.SUCCEED.equals(sandBoxExecuteResult.getRunStatus())) {
            // 对比执行结果, 时间限制, 空间限制
            userQuestionResultVO = doJudge(judgeSubmitDTO, sandBoxExecuteResult, userQuestionResultVO);
        } else {
            userQuestionResultVO.setPass(Constants.FALSE);
            if (sandBoxExecuteResult != null) {
                userQuestionResultVO.setExeMessage(sandBoxExecuteResult.getExeMessage());
            } else {
                userQuestionResultVO.setExeMessage(CodeRunStatus.UNKNOWN_FAILED.getMsg());
            }
            userQuestionResultVO.setScore(JudgeConstants.ERROR_SCORE);
        }

        saveUserSubmit(judgeSubmitDTO, userQuestionResultVO);
        return userQuestionResultVO;
    }

    /**
     * 判断代码是否满足通过条件
     */
    private UserQuestionResultVO doJudge(JudgeSubmitDTO judgeSubmitDTO, SandBoxExecuteResult sandBoxExecuteResult, UserQuestionResultVO userQuestionResultVO) {

        // 执行代码输出结果
        List<String> exeOutputList = sandBoxExecuteResult.getOutputList();

        // 预期输出结果
        List<String> outputList = judgeSubmitDTO.getOutputList();

        if (outputList.size() != exeOutputList.size()) {
            // 输出结果不一致, 直接不通过
            userQuestionResultVO.setScore(JudgeConstants.ERROR_SCORE);
            userQuestionResultVO.setPass(Constants.FALSE);
            userQuestionResultVO.setExeMessage(CodeRunStatus.NOT_ALL_PASSED.getMsg());
            return userQuestionResultVO;
        }

        // 判断测试用例是否通过
        List<UserExeResult> userExeResultList = new ArrayList<>();
        boolean passed = resultCompare(judgeSubmitDTO, outputList, exeOutputList, userExeResultList);
        return assembleUserQuestionResultVO(judgeSubmitDTO, sandBoxExecuteResult, userQuestionResultVO, userExeResultList, passed);
    }


    private UserQuestionResultVO assembleUserQuestionResultVO(JudgeSubmitDTO judgeSubmitDTO,
                                                              SandBoxExecuteResult sandBoxExecuteResult,
                                                              UserQuestionResultVO userQuestionResultVO,
                                                              List<UserExeResult> userExeResultList,
                                                              boolean passed) {
        userQuestionResultVO.setUserExeResultList(userExeResultList);
        // 用例不通过情况
        if (!passed) {
            userQuestionResultVO.setPass(Constants.FALSE);
            userQuestionResultVO.setScore(JudgeConstants.ERROR_SCORE);
            userQuestionResultVO.setExeMessage(CodeRunStatus.NOT_ALL_PASSED.getMsg());
            return userQuestionResultVO;
        }

        // 用例通过, 比较时间限制和空间限制
        if (sandBoxExecuteResult.getUseMemory() > judgeSubmitDTO.getSpaceLimit()) {
            userQuestionResultVO.setPass(Constants.FALSE);
            userQuestionResultVO.setScore(JudgeConstants.ERROR_SCORE);
            userQuestionResultVO.setExeMessage(CodeRunStatus.OUT_OF_MEMORY.getMsg());
            return userQuestionResultVO;
        }
        if (sandBoxExecuteResult.getUseTime() > judgeSubmitDTO.getTimeLimit()) {
            userQuestionResultVO.setPass(Constants.FALSE);
            userQuestionResultVO.setScore(JudgeConstants.ERROR_SCORE);
            userQuestionResultVO.setExeMessage(CodeRunStatus.OUT_OF_TIME.getMsg());
            return userQuestionResultVO;
        }

        // 通过
        userQuestionResultVO.setPass(Constants.TRUE);
        int score = judgeSubmitDTO.getDifficulty() * JudgeConstants.DEFAULT_SCORE;
        userQuestionResultVO.setScore(score);

        return userQuestionResultVO;
    }

    /**
     * 运行输出结果对比
     */
    private boolean resultCompare(JudgeSubmitDTO judgeSubmitDTO, List<String> outputList, List<String> exeOutputList, List<UserExeResult> userExeResultList) {
        boolean passed = true;
        for (int index = 0; index < outputList.size(); index++) {
            String output = outputList.get(index);
            String exeOutput = exeOutputList.get(index);
            String input = judgeSubmitDTO.getInputList().get(index);
            UserExeResult userExeResult = new UserExeResult();
            userExeResult.setInput(input);
            userExeResult.setOutput(output);
            userExeResult.setExeOutput(exeOutput);
            userExeResultList.add(userExeResult);
            if (!output.equals(exeOutput)) {
                passed = false;
                break;
            }
        }
        return passed;
    }

    /**
     * 保存本次提交记录
     */
    private void saveUserSubmit(JudgeSubmitDTO judgeSubmitDTO, UserQuestionResultVO userQuestionResultVO) {
        UserSubmit userSubmit = new UserSubmit();
        BeanUtils.copyProperties(userQuestionResultVO, userSubmit);
        userSubmit.setUserId(judgeSubmitDTO.getUserId());
        userSubmit.setUserCode(judgeSubmitDTO.getUserCode());
        userSubmit.setQuestionId(judgeSubmitDTO.getQuestionId());
        userSubmit.setExamId(judgeSubmitDTO.getExamId());
        userSubmit.setProgramType(judgeSubmitDTO.getProgramType());
        userSubmit.setUserCode(judgeSubmitDTO.getUserCode());
        userSubmit.setCaseJudgeRes(JSON.toJSONString(userQuestionResultVO.getUserExeResultList()));
        userSubmit.setCreateBy(judgeSubmitDTO.getUserId());  // 服务间调用, ThreadLocal 无法获取到用户id, 没有携带 token
        userSubmit.setUuid(judgeSubmitDTO.getUuid());
        userSubmit.setUserSubmitCode(judgeSubmitDTO.getUserSubmitCode());
        userSubmit.setMainFunc(judgeSubmitDTO.getMainFunc());

        // 删除上一次提交记录
//        userSubmitMapper.delete(new LambdaQueryWrapper<UserSubmit>()
//                .eq(UserSubmit::getUserId, judgeSubmitDTO.getUserId())
//                .eq(UserSubmit::getQuestionId, judgeSubmitDTO.getQuestionId())
//                .isNull(judgeSubmitDTO.getExamId() == null, UserSubmit::getExamId)
//                .eq(judgeSubmitDTO.getExamId() != null, UserSubmit::getExamId, judgeSubmitDTO.getExamId()));

        userSubmitMapper.insert(userSubmit);
    }
}
